﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using starLiGHT;

#endregion

#region COPYRIGHT

/*
    Copyright (c) 2008, 2009, 2010 
        Roland Rosenkranz (Glatzemann@email.de)
*/

#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Collision.

    starLiGHT.Collision is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Collision is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Collision.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
*/

#endregion

#region Box2D Copyright
/*
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 1486                  $:  Revision of last commit *
// * $Author:: glatzemann         $:  Author of last commit   *
// * $Date:: 2010-10-08 15:55:47 #$:  Date of last commit     *
// ************************************************************

// **************[ Box2D-Repository Info ]*********************
// Header-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Collision/Shapes/EdgeShape.h
// Revision   : r112
// Change-Date: 2010-06-14
//
// Source-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Collision/Shapes/EdgeShape.cpp
// Revision   : r132
// Change-Date: 2010-09-21
//
// Status     : Finished!
#endregion

namespace starLiGHT.Physic.TwoD
{
    public class EdgeShape : Shape
    {
        public EdgeShape()
        {
            m_type = ShapeType.EDGE;
            m_radius = PhysicSettings.PolygonRadius;
            m_hasVertex0 = false;
            m_hasVertex3 = false;
        }

        /// <summary>
        /// Set this as an isolated edge.
        /// </summary>
        public void Set(Vector2 v1, Vector2 v2)
        {
            m_vertex1 = v1;
            m_vertex2 = v2;
            m_hasVertex0 = false;
            m_hasVertex3 = false;
        }

        public override Shape Clone()
        {
            var clone = new EdgeShape();
            clone.m_type = m_type;
            clone.m_radius = m_radius;
            clone.m_vertex0 = m_vertex0;
            clone.m_vertex1 = m_vertex1;
            clone.m_vertex2 = m_vertex2;
            clone.m_vertex3 = m_vertex3;
            clone.m_hasVertex0 = m_hasVertex0;
            clone.m_hasVertex3 = m_hasVertex3;

            return clone;
        }

        public override int GetChildCount()
        {
            return 1;
        }

        public override bool TestPoint(ref Transform xf, Vector2 p)
        {
            return false;
        }

        // p = p1 + t * d
        // v = v1 + s * e
        // p1 + t * d = v1 + s * e
        // s * e - t * d = p1 - v1
        //

        public override bool RayCast(out RayCastOutput output, ref RayCastInput input, ref Transform xf, int childIndex)
        {
            output = new RayCastOutput();

            // Put the ray into the edge's frame of reference.
            Vector2 p1 = Math.MulT(xf.R, input.p1 - xf.position);
            Vector2 p2 = Math.MulT(xf.R, input.p2 - xf.position);
            Vector2 d = p2 - p1;

            Vector2 v1 = m_vertex1;
            Vector2 v2 = m_vertex2;
            Vector2 e = v2 - v1;
            Vector2 normal = new Vector2(e.Y, -e.X);
            normal.Normalize();

            // q = p1 + t * d
            // dot(normal, q - v1) = 0
            // dot(normal, p1 - v1) + t * dot(normal, d) = 0
            float numerator = Vector2.Dot(normal, v1 - p1);
            float denominator = Vector2.Dot(normal, d);

            if (denominator == 0.0f)
            {
                return false;
            }

            float t = numerator / denominator;
            if (t < 0.0f || 1.0f < t)
            {
                return false;
            }

            Vector2 q = p1 + t * d;

            // q = v1 + s * r
            // s = dot(q - v1, r) / dot(r, r)
            Vector2 r = v2 - v1;
            float rr = Vector2.Dot(r, r);
            if (rr == 0.0f)
            {
                return false;
            }

            float s = Vector2.Dot(q - v1, r) / rr;
            if (s < 0.0f || 1.0f < s)
            {
                return false;
            }

            output.fraction = t;
            if (numerator > 0.0f)
            {
                output.normal = -normal;
            }
            else
            {
                output.normal = normal;
            }
            return true;
        }

        public override void ComputeAABB(out AABB aabb, ref Transform xf, int childIndex)
        {
            Vector2 v1 = Math.Mul(xf, m_vertex1);
            Vector2 v2 = Math.Mul(xf, m_vertex2);

            Vector2 lower = Vector2.Min(v1, v2);
            Vector2 upper = Vector2.Max(v1, v2);

            Vector2 r = new Vector2(m_radius, m_radius);
            aabb.lowerBound = lower - r;
            aabb.upperBound = upper + r;
        }

        public override void ComputeMass(out MassData massData, float density)
        {
            massData.mass = 0.0f;
            massData.center = 0.5f * (m_vertex1 + m_vertex2);
            massData.I = 0.0f;
        }

        // These are the edge vertices
        internal Vector2 m_vertex1, m_vertex2;

        // Optional adjacent vertices. These are used for smooth collision.
        public Vector2 m_vertex0, m_vertex3;
        public bool m_hasVertex0, m_hasVertex3;
    }
}
